import os
import sys
import time
import json
import argparse
import logging
from datetime import datetime, timedelta
from threading import Timer

# 添加当前目录到系统路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# 导入自定义模块
from src.crawler import SportFieldCrawler as PlaygroundScraper
from src.database import StorageManager
from src.weather_service import WeatherService, WeatherConfigLoader
from src.data_handler import DataHandler

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(os.path.join(os.path.dirname(__file__), '..', 'logs', 'app.log')),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger('campus_running_app')


class CampusRunningApp:
    """
    校园跑步应用主类，集成所有功能模块
    提供操场开放时间查询、天气分析、跑步提醒等功能
    """
    
    def __init__(self, config_path=None):
        self.config_path = config_path or os.path.join(os.path.dirname(__file__), '..', 'config', 'app_config.json')
        self.config = self._load_config()
        
        # 初始化各个模块
        self._init_modules()
        
        # 定时任务配置
        self.scheduled_tasks = {}
        
        logger.info("校园跑步应用初始化完成")
    
    def _load_config(self):
        """加载应用配置"""
        # 确保配置目录存在
        os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
        
        # 默认配置
        default_config = {
            "app_name": "校园运动记录工具",
            "auto_scrape_enabled": True,
            "scrape_interval_hours": 24,
            "auto_weather_check_enabled": True,
            "weather_check_interval_hours": 3,
            "notification_enabled": True,
            "notification_time": "16:00",  # 下午4点发送当日跑步建议
            "school_name": "示例大学",
            "location": {
                "city": "北京",
                "latitude": 39.9042,
                "longitude": 116.4074
            }
        }
        
        # 如果配置文件存在，加载配置
        if os.path.exists(self.config_path):
            try:
                with open(self.config_path, 'r', encoding='utf-8') as f:
                    user_config = json.load(f)
                    # 合并默认配置和用户配置
                    default_config.update(user_config)
                    logger.info(f"成功加载配置文件: {self.config_path}")
            except Exception as e:
                logger.error(f"加载配置文件失败: {e}")
                logger.info("使用默认配置")
        
        # 保存配置（如果是首次运行）
        if not os.path.exists(self.config_path):
            try:
                with open(self.config_path, 'w', encoding='utf-8') as f:
                    json.dump(default_config, f, ensure_ascii=False, indent=2)
                logger.info(f"已创建默认配置文件: {self.config_path}")
            except Exception as e:
                logger.error(f"保存配置文件失败: {e}")
        
        return default_config
    
    def _init_modules(self):
        """初始化各个功能模块"""
        # 初始化存储管理器
        self.storage_manager = StorageManager(use_database=True)
        
        # 初始化数据处理器
        self.data_handler = DataHandler()
        
        # 初始化爬虫
        self.scraper = PlaygroundScraper(
            school_name=self.config["school_name"],
            storage_manager=self.storage_manager
        )
        
        # 初始化天气服务
        weather_config = WeatherConfigLoader.load_config()
        # 更新天气服务的位置信息
        weather_config["location"] = self.config["location"]
        self.weather_service = WeatherService(weather_config)
    
    def run_initial_setup(self):
        """运行初始设置"""
        logger.info("运行初始设置...")
        
        # 检查是否有开放时间数据，如果没有则爬取
        open_hours = self.storage_manager.get_open_hours()
        if not open_hours:
            logger.info("未找到操场开放时间数据，开始爬取...")
            self.scrape_playground_hours()
        else:
            logger.info("已存在操场开放时间数据")
        
        # 检查是否有路线数据，如果没有则初始化默认路线
        routes = self.storage_manager.get_routes()
        if not routes:
            logger.info("未找到跑步路线数据，初始化默认路线...")
            default_routes = self.data_handler._get_default_routes()
            self.storage_manager.save_routes(default_routes)
        else:
            logger.info(f"已存在{len(routes)}条跑步路线")
        
        logger.info("初始设置完成")
    
    def scrape_playground_hours(self):
        """爬取操场开放时间"""
        try:
            logger.info("开始爬取操场开放时间...")
            result = self.scraper.scrape()
            if result:
                logger.info("操场开放时间爬取成功")
                return True
            else:
                logger.warning("操场开放时间爬取失败，使用模拟数据")
                # 使用模拟数据
                mock_hours = self.data_handler._get_mock_open_hours()
                self.storage_manager.save_open_hours(mock_hours)
                return False
        except Exception as e:
            logger.error(f"爬取操场开放时间时出错: {e}")
            return False
    
    def get_today_running_recommendation(self):
        """
        获取今日跑步建议
        结合天气数据和操场开放时间生成综合建议
        """
        try:
            # 获取当前天气
            weather_data = self.weather_service.get_weather_data()
            
            # 分析天气是否适合跑步
            weather_analysis = self.weather_service.is_good_for_running(weather_data)
            
            # 获取操场开放时间
            today = datetime.now().strftime("%A")  # 获取今天是星期几（英文）
            open_hours = self.storage_manager.get_open_hours()
            
            # 获取今日开放时间
            today_hours = None
            for day, data in open_hours.items():
                if day.lower() in today.lower() or self._get_weekday_english(day) == today:
                    today_hours = data
                    break
            
            # 生成建议文本
            recommendation = self._generate_recommendation_text(
                weather_data,
                weather_analysis,
                today_hours
            )
            
            return recommendation
        except Exception as e:
            logger.error(f"生成跑步建议时出错: {e}")
            return {"error": str(e)}
    
    def _generate_recommendation_text(self, weather_data, weather_analysis, today_hours):
        """生成建议文本"""
        # 构建建议内容
        recommendation = {
            "date": datetime.now().strftime("%Y-%m-%d"),
            "today": self._get_today_weekday(),
            "weather_summary": weather_analysis["weather_summary"],
            "suitability_level": weather_analysis["suitability_level"],
            "suitability_score": weather_analysis["suitability_score"],
            "playground_open_hours": self._format_open_hours(today_hours),
            "reasons": weather_analysis["reasons"],
            "recommendations": weather_analysis["recommendations"],
            "detailed_weather": weather_data,
        }
        
        return recommendation
    
    def _get_today_weekday(self):
        """获取今天的星期几（中文）"""
        weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        return weekdays[datetime.now().weekday()]
    
    def _get_weekday_english(self, weekday_chinese):
        """将中文星期转换为英文"""
        mapping = {
            '周一': 'Monday',
            '周二': 'Tuesday',
            '周三': 'Wednesday',
            '周四': 'Thursday',
            '周五': 'Friday',
            '周六': 'Saturday',
            '周日': 'Sunday',
        }
        return mapping.get(weekday_chinese, '')
    
    def _format_open_hours(self, hours_data):
        """格式化开放时间文本"""
        if not hours_data:
            return "未查询到今日开放时间"
        
        slots = hours_data.get('time_slots', [])
        if not slots:
            return "今日无开放时间"
        
        formatted_slots = []
        for slot in slots:
            formatted_slots.append(f"{slot['start']} - {slot['end']}")
        
        open_text = "今日开放时间: " + "，".join(formatted_slots)
        if 'note' in hours_data and hours_data['note']:
            open_text += f"\n备注: {hours_data['note']}"
        
        return open_text
    
    def display_running_recommendation(self, recommendation=None):
        """显示跑步建议"""
        if not recommendation:
            recommendation = self.get_today_running_recommendation()
        
        if "error" in recommendation:
            print(f"生成建议时出错: {recommendation['error']}")
            return
        
        print("=" * 60)
        print(f"🏃‍♂️ {self.config['app_name']} - 今日跑步建议 🏃‍♀️")
        print("=" * 60)
        print(f"📅 {recommendation['date']} {recommendation['today']}")
        print(f"🌤️ {recommendation['weather_summary']}")
        print(f"🏃‍♂️ 跑步适宜度: {recommendation['suitability_level']} ({recommendation['suitability_score']}%)")
        print(f"⚽ {recommendation['playground_open_hours']}")
        print()
        
        if recommendation['reasons']:
            print("📋 影响因素:")
            for reason in recommendation['reasons']:
                print(f"  • {reason}")
            print()
        
        print("💡 跑步建议:")
        for rec in recommendation['recommendations']:
            print(f"  • {rec}")
        print()
        
        # 根据适宜度给出总结性建议
        if recommendation['suitability_score'] >= 80:
            print("🎉 今日非常适合跑步！建议您尽快安排时间进行户外运动。")
        elif recommendation['suitability_score'] >= 60:
            print("👍 今日适合跑步，是进行户外运动的好日子。")
        elif recommendation['suitability_score'] >= 40:
            print("🙂 今日较适合跑步，如果有计划可以适当调整跑步强度。")
        elif recommendation['suitability_score'] >= 20:
            print("👎 今日不太适合跑步，建议减少户外运动时间或改为室内活动。")
        else:
            print("❌ 今日不适合跑步，建议您选择其他室内运动方式。")
        
        print("=" * 60)
    
    def schedule_tasks(self):
        """设置定时任务"""
        logger.info("设置定时任务...")
        
        # 定时爬取操场开放时间
        if self.config["auto_scrape_enabled"]:
            self._schedule_task(
                "scrape_hours",
                self.scrape_playground_hours,
                interval_hours=self.config["scrape_interval_hours"]
            )
        
        # 定时检查天气并发送提醒
        if self.config["notification_enabled"]:
            # 解析通知时间
            hour, minute = map(int, self.config["notification_time"].split(":"))
            self._schedule_daily_task(
                "daily_reminder",
                self.send_daily_reminder,
                hour=hour,
                minute=minute
            )
        
        logger.info("定时任务设置完成")
    
    def _schedule_task(self, task_name, task_function, interval_hours):
        """安排周期性任务"""
        def task_wrapper():
            try:
                task_function()
            except Exception as e:
                logger.error(f"执行定时任务 {task_name} 时出错: {e}")
            finally:
                # 重新安排任务
                interval_seconds = interval_hours * 3600
                self.scheduled_tasks[task_name] = Timer(interval_seconds, task_wrapper)
                self.scheduled_tasks[task_name].daemon = True
                self.scheduled_tasks[task_name].start()
        
        # 立即执行一次
        task_wrapper()
    
    def _schedule_daily_task(self, task_name, task_function, hour, minute):
        """安排每日固定时间的任务"""
        def task_wrapper():
            try:
                task_function()
            except Exception as e:
                logger.error(f"执行每日任务 {task_name} 时出错: {e}")
            finally:
                # 计算下次执行时间（明天同一时间）
                now = datetime.now()
                next_run = now.replace(hour=hour, minute=minute, second=0, microsecond=0) + timedelta(days=1)
                wait_seconds = (next_run - now).total_seconds()
                
                self.scheduled_tasks[task_name] = Timer(wait_seconds, task_wrapper)
                self.scheduled_tasks[task_name].daemon = True
                self.scheduled_tasks[task_name].start()
        
        # 计算首次执行时间
        now = datetime.now()
        first_run = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
        
        # 如果时间已过，安排到明天
        if first_run < now:
            first_run += timedelta(days=1)
        
        wait_seconds = (first_run - now).total_seconds()
        
        logger.info(f"任务 {task_name} 将在 {first_run.strftime('%Y-%m-%d %H:%M:%S')} 执行")
        
        self.scheduled_tasks[task_name] = Timer(wait_seconds, task_wrapper)
        self.scheduled_tasks[task_name].daemon = True
        self.scheduled_tasks[task_name].start()
    
    def send_daily_reminder(self):
        """发送每日跑步提醒"""
        try:
            logger.info("生成并发送每日跑步提醒...")
            
            # 获取今日跑步建议
            recommendation = self.get_today_running_recommendation()
            
            # 保存提醒记录
            reminder_data = {
                "timestamp": datetime.now().isoformat(),
                "recommendation": recommendation
            }
            
            # 保存到文件
            reminders_dir = os.path.join(os.path.dirname(__file__), '..', 'data', 'reminders')
            os.makedirs(reminders_dir, exist_ok=True)
            
            reminder_file = os.path.join(
                reminders_dir, 
                f"reminder_{datetime.now().strftime('%Y%m%d')}.json"
            )
            
            with open(reminder_file, 'w', encoding='utf-8') as f:
                json.dump(reminder_data, f, ensure_ascii=False, indent=2)
            
            # 在控制台显示提醒
            self.display_running_recommendation(recommendation)
            
            logger.info(f"每日跑步提醒已生成: {reminder_file}")
        except Exception as e:
            logger.error(f"发送每日提醒时出错: {e}")
    
    def save_recommendation(self, recommendation=None, output_path=None):
        """保存跑步建议到文件"""
        if not recommendation:
            recommendation = self.get_today_running_recommendation()
        
        if output_path is None:
            output_dir = os.path.join(os.path.dirname(__file__), '..', 'data', 'recommendations')
            os.makedirs(output_dir, exist_ok=True)
            output_path = os.path.join(
                output_dir,
                f"recommendation_{datetime.now().strftime('%Y%m%d')}.json"
            )
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(recommendation, f, ensure_ascii=False, indent=2)
            logger.info(f"跑步建议已保存到: {output_path}")
            return True
        except Exception as e:
            logger.error(f"保存跑步建议时出错: {e}")
            return False
    
    def run_cli(self):
        """运行命令行界面"""
        print(f"\n欢迎使用 {self.config['app_name']}！\n")
        print("1. 获取今日跑步建议")
        print("2. 刷新操场开放时间")
        print("3. 保存今日跑步建议到文件")
        print("4. 查看可用跑步路线")
        print("5. 退出程序")
        
        while True:
            try:
                choice = input("\n请输入操作编号 (1-5): ")
                
                if choice == '1':
                    self.display_running_recommendation()
                elif choice == '2':
                    print("正在刷新操场开放时间...")
                    success = self.scrape_playground_hours()
                    if success:
                        print("操场开放时间已更新！")
                    else:
                        print("操场开放时间更新失败，可能使用的是模拟数据。")
                elif choice == '3':
                    output_path = input("请输入保存路径 (直接回车使用默认路径): ") or None
                    success = self.save_recommendation(output_path=output_path)
                    if success:
                        print("跑步建议已保存成功！")
                    else:
                        print("跑步建议保存失败。")
                elif choice == '4':
                    self._display_available_routes()
                elif choice == '5':
                    print("感谢使用，再见！")
                    break
                else:
                    print("无效的选择，请重新输入。")
            except KeyboardInterrupt:
                print("\n程序已中断。")
                break
            except Exception as e:
                print(f"发生错误: {e}")
    
    def _display_available_routes(self):
        """显示可用的跑步路线"""
        routes = self.storage_manager.get_routes()
        
        if not routes:
            print("暂无可用跑步路线。")
            return
        
        print(f"\n可用跑步路线 ({len(routes)}条):\n")
        
        for i, route in enumerate(routes, 1):
            print(f"{i}. {route['name']}")
            print(f"   距离: {route['distance']} 公里")
            print(f"   难度: {route['difficulty']}")
            if 'elevation_gain' in route and route['elevation_gain'] > 0:
                print(f"   爬升: {route['elevation_gain']} 米")
            if 'description' in route and route['description']:
                print(f"   描述: {route['description']}")
            print()
    
    def run(self, mode='interactive'):
        """
        运行应用
        mode: 'interactive' - 交互模式
             'daemon' - 守护进程模式（后台运行）
             'once' - 只运行一次并退出
        """
        try:
            # 运行初始设置
            self.run_initial_setup()
            
            if mode == 'interactive':
                # 运行命令行界面
                self.run_cli()
            elif mode == 'daemon':
                # 设置定时任务并保持运行
                self.schedule_tasks()
                print(f"{self.config['app_name']} 已启动，正在后台运行...")
                print("按 Ctrl+C 停止程序")
                
                # 保持程序运行
                try:
                    while True:
                        time.sleep(1)
                except KeyboardInterrupt:
                    print("程序已停止")
            elif mode == 'once':
                # 只生成一次建议并退出
                self.display_running_recommendation()
                self.save_recommendation()
        finally:
            # 清理定时任务
            self._cleanup_scheduled_tasks()
    
    def _cleanup_scheduled_tasks(self):
        """清理定时任务"""
        for task_name, task in self.scheduled_tasks.items():
            if task.is_alive():
                task.cancel()
                logger.info(f"已取消定时任务: {task_name}")


def main():
    """主函数，处理命令行参数"""
    parser = argparse.ArgumentParser(description="校园运动记录工具 - 提供操场开放时间查询和跑步建议")
    
    parser.add_argument(
        '--mode', 
        choices=['interactive', 'daemon', 'once'],
        default='interactive',
        help='运行模式 (interactive: 交互模式, daemon: 后台运行, once: 生成一次建议后退出)'
    )
    
    parser.add_argument(
        '--config',
        help='配置文件路径'
    )
    
    parser.add_argument(
        '--scrape',
        action='store_true',
        help='立即爬取操场开放时间'
    )
    
    args = parser.parse_args()
    
    # 创建应用实例
    app = CampusRunningApp(config_path=args.config)
    
    # 如果指定了立即爬取
    if args.scrape:
        app.scrape_playground_hours()
        return
    
    # 运行应用
    app.run(mode=args.mode)


if __name__ == "__main__":
    main()